package br.ufrj.ppgi.infovis.mtgraph;

import java.awt.event.KeyEvent;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.Vector;

//import mri.v3ds.Scene3ds;

import org.mt4j.AbstractMTApplication;
import org.mt4j.MTApplication;
import org.mt4j.components.MTCanvas;
import org.mt4j.components.MTComponent;
import org.mt4j.components.visibleComponents.shapes.MTEllipse;
import org.mt4j.components.visibleComponents.shapes.MTLine;
import org.mt4j.components.visibleComponents.widgets.MTBackgroundImage;
import org.mt4j.components.visibleComponents.widgets.MTTextArea;
import org.mt4j.components.visibleComponents.widgets.buttons.MTImageButton;
import org.mt4j.components.visibleComponents.widgets.keyboard.ITextInputListener;
import org.mt4j.input.inputData.MTInputEvent;
import org.mt4j.input.inputProcessors.IGestureEventListener;
import org.mt4j.input.inputProcessors.MTGestureEvent;
import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapEvent;
import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapProcessor;
import org.mt4j.input.inputProcessors.globalProcessors.AbstractGlobalInputProcessor;
import org.mt4j.input.inputProcessors.globalProcessors.CursorTracer;
import org.mt4j.sceneManagement.AbstractScene;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.MTColor;
import org.mt4j.util.camera.IFrustum;
import org.mt4j.util.camera.MTCamera;
import org.mt4j.util.font.FontManager;
import org.mt4j.util.font.fontFactories.BitmapFontFactory;
import org.mt4j.util.math.ToolsMath;
import org.mt4j.util.math.Vector3D;

import processing.core.PGraphics;
import processing.core.PImage;
import br.ufrj.ppgi.infovis.mtgraph.shapes.LabeledCircle;
import br.ufrj.ppgi.infovis.styles.Category;
import br.ufrj.ppgi.infovis.styles.Configuration;
import br.ufrj.ppgi.infovis.toolbar.MTToolbarButton;
import br.ufrj.ppgi.infovis.util.TextureCache;
import br.ufrj.ppgi.infovis.util.XMLConfigImport;
import br.ufrj.ppgi.infovis.util.XMLExport;
import br.ufrj.ppgi.infovis.mtgraph.GraphNodeStack;
import br.ufrj.ppgi.infovis.mtgraph.GraphEdgeStack;

/**
 * @author Ewoud van Pareren
 * 
 * This Scene is designed to hold a Tree instance.
 * It handles most of the GUI interaction and serves as a bridge between
 * a Tree and an MT4j application.
 */
public class GraphScene extends AbstractScene {
	
	private int valid = 0;
	private int new_count = 0;
	
	private float node_radius = 30f;
	private MTApplication app;
	private Graph graph;	
	private Map<MTComponent, GraphNode> mapping;
	static Vector<Vector3D> canvas_places = new Vector<Vector3D>();
	private int canvas_places_size = (int) ((node_radius * 2) + 20);
	private ITextInputListener txtInputListener;
	private Stack<GraphNode> StackNode;
	private Boolean IsSelectionEvent = false;
	private enum eventType {NONE, LINK_NODE, UNLINK_NODE};
	public eventType event;
	private GraphArea graphArea;
	
	private MTToolbarButton clickedTBB = null; 
	
	private Configuration config;
	
	
/*-------------------------------------------------*/
/*---------------------CONTROLE CENA---------------*/
/*-------------------------------------------------*/
	/**
	 * Get the tree attached to this scene.
	 * 
	 * @return the Tree instance attached to this scene
	 */
	public Graph getGraph() {
		return graph;
	}

	/**
	 * To be called from Tree: attach this Tree instance.
	 * 
	 * This method is not for use by any application.
	 * Use Tree.setTreeScene() instead.
	 * 
	 * @param graph
	 */
	protected void setGraph(Graph graph) {
		this.graph = graph;
	}

	/**
	 * Constructor.
	 * 
	 * @param mtApplication the MTApplication to use
	 * @param name the name of the Scene
	 */
	public GraphScene(MTApplication mtApplication, String name) {
		super(mtApplication, name);
		this.app = mtApplication;
		this.setName("GraphScene");
		this.setClearColor(MTColor.SILVER);
		this.registerGlobalInputProcessor(new CursorTracer(app, this));
		//this.getCanvas().addGestureListener(null, null);
		
		graphArea = new GraphArea(app, 100, 20, app.getWidth()-200, app.getHeight()-40);
		
		
		
		
		MTBackgroundImage background = new MTBackgroundImage(mtApplication, TextureCache.getTexture(TextureCache.BG_WOOD), true);
		getCanvas().addChild(background);
		
		//graphArea.setTexture(TextureCache.getTexture(TextureCache.BG_WOOD));
		graphArea.setFillColor(new MTColor(10, 10, 0, MTColor.ALPHA_HIGH_TRANSPARENCY));
		graphArea.setNoFill(false);
		
		//MTEllipse circle = new MTEllipse(app, new Vector3D(300, 300, 0), 100, 100);
		//circle.setFillColor(MTColor.AQUA);
		//graphArea.addChild(circle);
		
		// Invalidate on interaction
		this.registerGlobalInputProcessor(new AbstractGlobalInputProcessor() {
			@Override
			public void processInputEvtImpl(MTInputEvent arg0) {
				invalidate();
			}
		});
		
		this.getCanvas().setFrustumCulling(true);
		
		FontManager.getInstance().registerFontFactory(".ttf", new BitmapFontFactory());
		
		getCanvas().registerInputProcessor(new TapProcessor(this.getMTApplication(), 25));
		getCanvas().addGestureListener(TapProcessor.class, new IGestureEventListener() {
			public boolean processGestureEvent(MTGestureEvent ge) {
				TapEvent te = (TapEvent)ge;
				if (te.isTapped()){
					deselectionNodes();
				}
				return false;
			}
		});

		//Define os poss�veis lugares na tela para os n�s importados
		int x = 150;
		int y = 55;
		for (int i=0; y < 750; i++) {			
			this.canvas_places.add(new Vector3D(x,y,0));
			x += canvas_places_size;
			if (x > 900) {
				x = 150;
				y+= canvas_places_size;
			}
		}
		
		/*MTImageButton bt_add = makeAddButton();
		getCanvas().addChild(bt_add);
		MTImageButton bt_export = makeExportButton();
		getCanvas().addChild(bt_export);
		
		ButtonRemove bt_rm = new ButtonRemove();
		MTImageButton remove_bt = bt_rm.makeButtonRemove(app);
		getCanvas().addChild(remove_bt);
		*/
		
		// Imports style scene xml
		XMLConfigImport configImporter = new XMLConfigImport("src/br/ufrj/ppgi/infovis/data/config.xml");
		this.setConfig(configImporter.Import());		
		
		
		//TOOLBAR		
		GraphToolBar gtb = new GraphToolBar(this);

		
		gtb.AddDefaultButtons();
		
		mapping = new HashMap<MTComponent, GraphNode>();
		
		this.StackNode = new Stack<GraphNode>();
		this.getCanvas().addChild(graphArea);
	}

	
	/**
	 * Built-in invalidation system.
	 * 
	 * Typically, this is called on user interaction, after tree updates
	 * and in other situations where redrawing is needed.
	 */
	public void invalidate() {
		valid = 10;
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.mt4j.sceneManagement.AbstractScene#drawAndUpdate(processing.core.PGraphics, long)
	 */
	public void drawAndUpdate(PGraphics given, long time) {
		if (valid > 0) {
			super.drawAndUpdate(given, time);
			valid--;
		}
		
		
		
		/*Aten�ao: Logica de selecao de NOS 
		 * Quando clicado nos botoes de Linkar ou Deslinkar NOS atraves do Toolbar, 
		 * passar� a executar o codigo abaixo e deacordo com o "evento" selecionado dever� 
		 * chamar o procedimento destinado para cada a�ao com os nos guardados na pilha.
		 * 
		 * Para PARAR a execu�ao dessa logica de Sele�ao de NOS, � necessario clicar no fundo
		 * do grafo (Cenario), que ir� apagar os nos guardados na pilha.
		 * */
		if (this.IsSelectionEvent) {
			//Pega os nos selecionados
			GraphNode[] getAll = getAllNodes();
			for (GraphNode n: getAll) {
				if (n.getSelected()) {
					//Salva na pilha
					GraphNodeStack.showpush(this.StackNode, n);
				}
			}	
			
			//Realiza um evento de acordo com o botao selecinado no toolbar
	        switch (this.event) {
		        case NONE:
		        	deselectionNodes();
		        	//System.out.println("deselectionNodes()");
		            break;
		        case LINK_NODE:
					try {
						this.NewEdge();
					} catch (GraphException e) {
						e.printStackTrace();
					}
		            break;
		        case UNLINK_NODE:
					try {
						this.DeleteEdge();
					} catch (Exception e) {
						e.printStackTrace();
					}
		            break;	           
		        default:  
		            break;  
	        }
		}
	}
	
	/**
	 * Initialize.
	 * 
	 * Currently unused.
	 */
	@Override
	public void init() {
	}

	/**
	 * Shutdown cleanup.
	 * 
	 * Currently unused.
	 */
	@Override
	public void shutDown() {

	}
	
	public void resetCamera() {
		try {
			((MTCamera)getSceneCam()).resetToDefault();
		} catch (ClassCastException err) {
			
		}
	}
	
	
/*-------------------------------------------------*/
/*---------------------CONTROLE GRAFO--------------*/
/*-------------------------------------------------*/
	
	/**
	 * Add a Node to the Scene.
	 * 
	 * Note that this also places the given component-childNode pair
	 * in a hashmap, to allow looking up Nodes via components.
	 * 
	 * Typically called by Tree.
	 * If you want to add a Node to this scene, do it through the Tree.
	 * If you want to add a different kind of component, use addChild(MTComponent). 
	 * 
	 * @param child the Node to add
	 */
	protected void addNode(GraphNode node) {
		MTComponent in = node.getComponent();
		//Gestures	
		//node.setComponentListeners(this);
		
		graphArea.addChild(in);
		mapping.put(in, node);
	}
	
	// TODO: addEdge
	protected void addEdge(GraphEdge edge) {
		edge.updateLines();
		MTComponent in = edge.getComponent();
		
		GraphNode[] nodes = edge.getNodes();
		
		//Gestures	
		//edge.setComponentListeners(this);	
		
		graphArea.addChild(in);
		
		nodes[0].getComponent().sendToFront();
		nodes[1].getComponent().sendToFront();
		
	}
	
	/**
	 * Add a component as child.
	 * 
	 * This is used for non-node components, like lines, GUI and
	 * other things like that.
	 * 
	 * @param in the component to add to the scene
	 */
	public void addChild(MTComponent in) {
		//clearAllGestures(in);
		getCanvas().addChild(in);
	}
	
	/**
	 * Remove the child component from the scene.
	 * 
	 * Used internally. May be used externally to remove some components.
	 * 
	 * Warning: be careful not to remove node components.
	 * That is handled by the Tree system
	 * (remove/hide its corresponding Node and call Tree.update()).
	 * 
	 * @param in the component to remove from the scene
	 */
	
	/* TODO: fazer metodo para remover arestas */
	public void removeChild(MTComponent in) {
		// For safety, remove all listeners
		in.unregisterAllInputProcessors();
		in.removeAllGestureEventListeners();
		
		graphArea.removeChild(in);
		
		GraphNode check = mapping.get(in);
		//if (check != null && check.isChild() && check.getEdge().getComponent() != null) {
		//	removeChild(check.getEdge().getComponent());
		//}
		mapping.remove(in);
	}
	
	/**
	 * Generate a default component.
	 * 
	 * This is typically used to generate a standard Node component.
	 * 
	 * @return a default MTRectangle component
	 */
	public LabeledCircle makeDefaultComponent() {
		return makeDefaultComponent("Nome");
	}
	
	/**
	 * Generate a default component with custom text.
	 * 
	 * 
	 * @param in the text to be used
	 * @return a default MTRectangle component (with custom text)
	 */
	public LabeledCircle makeDefaultComponent(String in) {
		MTColor col = new MTColor(ToolsMath.getRandom(60, 255), ToolsMath.getRandom(60, 255),ToolsMath.getRandom(60, 255), 200);
		LabeledCircle lc = new LabeledCircle(app, col, 
				node_radius, 
				FontManager.getInstance().createFont(app, "arial.ttf", 14, MTColor.WHITE), 
				in);
		
	
		lc.scaleGlobal(graphArea.getScaleFactor(), graphArea.getScaleFactor(), 
				graphArea.getScaleFactor(),lc.getCenterPointGlobal());
		
		
		
		return lc;
	}
	
	/**
	 * Construct a default line.
	 * 
	 * This generates a simple MTLine of 0px (aka 1px regardless of transformation).
	 * 
	 * This method is typically used to populate Edge objects with components.
	 * 
	 * Note that the returned Line is customized to prevent frustum culling
	 * when its start and end points are not in view.
	 * 
	 * @return a simple MTLine
	 */
	public MTLine makeDefaultLine() {
		
		MTLine returnThis = new MTLine(app, 0,0,1,1) {
			// Override the frustum culling check
			// Otherwise, lines are not visible if their appropiate nodes aren't
			public boolean isContainedIn(IFrustum frustum) {
				float halfsize = this.getLength()/2;
				
				return (frustum.isSphereInFrustum(getCenterPointGlobal(), halfsize) != IFrustum.OUTSIDE); // Always draw the lines?
			}
		};
		returnThis.setStrokeColor(MTColor.WHITE);
		//returnThis.setUseVBOs(true);
		returnThis.unregisterAllInputProcessors();
		returnThis.removeAllGestureEventListeners();
		returnThis.setPickable(false);
		
		Vector3D pos;
		pos = returnThis.getCenterPointGlobal();
		pos.setZ(0f);
		returnThis.setPositionGlobal(pos);
		
		return returnThis;
	}
	
	/**
	 * Remove all node components.
	 * 
	 * This is used upon attaching/detaching Tree instances.
	 */
	protected void removeAllNodeComponents() {
		GraphNode[] getAll = getAllNodes();
		for (GraphNode n: getAll) {
			removeOneNodeComponent(n);
		}
	}
	
	/**
	 * Remove one node component.
	 */
	protected void removeOneNodeComponent(GraphNode Node) {	
		Graph graph = Node.getGraph();
		AdjacentEntry[] allEdges = Node.getAllAdjacentNodes();
		for (AdjacentEntry e: allEdges) {
			GraphEdgeManager.removeGraphEdge(e.getEdge());
			removeChild(e.getEdge().getComponent());	
		}
		graph.removeNode(Node);
		removeChild(Node.getComponent());
		this.invalidate();
	}

	/**
	 * Remove one edge component.
	 */
	protected void removeOneEdgeComponent(GraphEdge Edge) {
		Graph graph = Edge.getGraph();
		GraphEdgeManager.removeGraphEdge(Edge);
		removeChild(Edge.getComponent());
		graph.removeEdge(Edge);
		removeChild(Edge.getComponent());
		this.invalidate();
	}
	
	/**
	 * Add one edge component.
	 */
	protected void addOneNodeComponent(GraphNode Node1, GraphNode Node2) {
		try {
			GraphEdge new_edge = GraphEdgeManager.makeGraphEdge(Node1, Node2);
			graph.getGraphScene().addEdge(new_edge);
		} catch (GraphException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}	
	
	
	/**
	 * Get all nodes in the scene.
	 * 
	 * This is used upon attaching/detaching Tree instances.
	 */
	protected GraphNode[] getAllNodes() {
		GraphNode[] getAll = mapping.values().toArray(new GraphNode[0]);
		return getAll;
	}
	
	protected GraphEdge[] getAllEdges() {
		GraphEdge[] getAll = graph.getEdgeList();
		return getAll;
	}
	
	private MTImageButton makeAddButton() {	
		String img_prefix= "br" +  AbstractMTApplication.separator +
				"ufrj" +  AbstractMTApplication.separator + "ppgi" + AbstractMTApplication.separator + 
				"infovis" +  AbstractMTApplication.separator + "data" +  AbstractMTApplication.separator;
		PImage bt_mais_img = app.loadImage(img_prefix + "bt_mais.png");
		final MTImageButton bt_add = new MTImageButton(app, bt_mais_img);
		bt_add.setName("KeyboardButton");
		bt_add.setNoStroke(true);
		bt_add.setPositionGlobal(new Vector3D(40,40,0));
		bt_add.setSizeXYGlobal(50,50);
		bt_add.addGestureListener(TapProcessor.class, new IGestureEventListener() {
			@Override
			public boolean processGestureEvent(MTGestureEvent ge) {
				TapEvent te = (TapEvent)ge;
				switch (te.getTapID()) {
				case TapEvent.TAPPED:
						//cria GraphNode
						Integer nc = new_count;
						
						GraphNode node = new GraphNode();
						node.setGraph(graph);
						node.setComponent();
						LabeledCircle lblc = node.getComponent();
						float width = MT4jSettings.getInstance().getWindowWidth();
						float height = MT4jSettings.getInstance().getWindowHeight();
						float margin = 50;  // TODO: generalizar
						lblc.setPositionGlobal(new Vector3D(width - margin/2 - 40, height - margin/2 - 40, 0));
						lblc.setLabel("Novo" + nc.toString());
						MTColor color = new MTColor(ToolsMath.getRandom(60, 255),
								ToolsMath.getRandom(60, 255),ToolsMath.getRandom(60, 255));
						
						
						float rand = ToolsMath.getRandom(0, 100);
						if ( rand <= 50) {
							int chosen = (int) ToolsMath.getRandom(0, graph.countNodes());
							addOneNodeComponent(node, graph.getNodeList()[chosen]);
						}		
						graphArea.addChild(lblc);
						graph.getGraphScene().addNode(node);
						graph.addNode(node);
						new_count = (new_count + 1) % 10;
						//graph.getGraphScene().invalidate();
					break;
				default:
					break;
				}
				return true;
			}
		});
		return bt_add;
	}
	
	/*private MTImageButton makeExportButton () {
		String img_prefix = "br" +  AbstractMTApplication.separator +
				"ufrj" +  AbstractMTApplication.separator + "ppgi" + AbstractMTApplication.separator + 
				"infovis" +  AbstractMTApplication.separator + "data" +  AbstractMTApplication.separator;
		PImage bt_mais_img = app.loadImage(img_prefix + "bt_export.png");
		final MTImageButton bt_export = new MTImageButton(app, bt_mais_img);
		bt_export.setName("KeyboardButton");
		bt_export.setNoStroke(true);
		bt_export.setPositionGlobal(new Vector3D(100,40,0));
		bt_export.setSizeXYGlobal(50,50);
		
		bt_export.addGestureListener(TapProcessor.class, new IGestureEventListener() {
			@Override
			public boolean processGestureEvent(MTGestureEvent ge) {
				TapEvent te = (TapEvent)ge;
				switch (te.getTapID()) {
				case TapEvent.TAPPED:
					XMLExport exporter = new XMLExport();
					exporter.Export();
					break;
				default:
					break;
				}
				return true;
			}
		});
		return bt_export;
	}*/
	
	

/*-------------------------------------------------*/
/*------------CONTROLE PARA EVENTOS DOS BOTOES-----*/
/*-------------------------------------------------*/
	//Habilita a "selecao" de nos
    public void setSelectionEvent(Boolean sel) {
    	if (!sel) {
    		if (clickedTBB != null)
    			clickedTBB.setFillColor(MTColor.WHITE);
    	}
    	this.IsSelectionEvent = sel;
    	System.out.println("clicar: "+sel.toString());
    }
    
    public Boolean getSelectionEvent() {
    	return this.IsSelectionEvent;
    }
    
    //Seta ambiente p/ nenhum evento
    public void setEventNone() {
    	this.event = eventType.NONE;    	
    	System.out.println("parou de clicar");
    }
    
    //Seta ambiente p/ evento Linkar Nos
    public void setEventLinkNode() {
    	this.event = eventType.LINK_NODE;    	
    }
    
    //Seta ambiente p/ evento Deslinkar Nos
    public void setEventUnlinkNode() {
    	this.event = eventType.UNLINK_NODE;    	
    }
    
    //Deseleciona os Nos do grafo
	public void deselectionNodes() {
		//Percorre todos os n�s e seta com n�o selecionados, quando clicado no fundo do grafo
		GraphNode[] getAll = getAllNodes();
		for (GraphNode n: getAll) {
			n.setSelected(false);
			//Apaga na pilha
			try { 
				GraphNodeStack.showpop(this.StackNode); 
			} catch (EmptyStackException e) { 
				System.out.println("empty stack"); 
			}
		}
		this.setSelectionEvent(false);
	}


/*-------------------------------------------------*/
/*-------------EVENTOS DOS BOTOES TOOLBAR----------*/
/*-------------------------------------------------*/
	//Cria um No (Chamado pelo ToolBar)
	public void NewNode(Category type) {
		//cria GraphNode
		//Integer nc = new_count;
		GraphNode node = new GraphNode();
		node.setId(graph.generateNewNodeId());
		node.setGraph(graph);
		node.setType(type.getName());
		node.setComponent();
		LabeledCircle lblc = node.getComponent();
		float width = MT4jSettings.getInstance().getWindowWidth();
		float height = MT4jSettings.getInstance().getWindowHeight();
		float margin = 50;  // TODO: generalizar
		lblc.setPositionGlobal(new Vector3D((width - margin/2)/2, (height - margin/2)-30));
		//lblc.setLabel("Novo" + nc.toString());
		lblc.setLabel("Nome");
		node.setLabel("Nome");
		
		
		/*
		MTColor color = new MTColor(ToolsMath.getRandom(60, 255),
				ToolsMath.getRandom(60, 255),ToolsMath.getRandom(60, 255), 200);
		*/	
		MTColor color = type.getColor();
		lblc.setColor(color);
		lblc.setNoStroke(true);
		PGraphics gr = new PGraphics();
		lblc.drawComponent(gr);
		
/*		COMENTADO PARA NAO CRIAR ARESTAS AUTOMATICAS NA CRIACAO DE NOS
 * 		float rand = ToolsMath.getRandom(0, 100);
		if (graph.countNodes() > 1) {
			if ( rand <= 50) {
				int chosen = (int) ToolsMath.getRandom(0, graph.countNodes());
				addOneNodeComponent(node, graph.getNodeList()[chosen]);
			}
		}*/

		graph.getGraphScene().addNode(node);
		graph.addNode(node);
		new_count = (new_count + 1) % 10;
		//graph.getGraphScene().invalidate();
	}
	
	//Remover No(s) (Chamado pelo ToolBar ou Tap&Hold)
	public void DeleteNode(boolean allNodes, GraphNode oneNode) {
		if (allNodes) {
			removeAllNodeComponents();	
		} else {
			//Caso selecionar um no somente
			removeOneNodeComponent(oneNode);			
		}		
	}

	//Cria uma Aresta (Chamado pelo ToolBar)
    public void NewEdge() throws GraphException {
    	if (!this.StackNode.isEmpty()) {
    		GraphNode node1 = GraphNodeStack.showpop(this.StackNode);
    		GraphNode node2 = GraphNodeStack.showpop(this.StackNode);
    		if ((node1 != null) && (node2 != null)) {
				addOneNodeComponent(node1, node2);
        		this.setEventNone();       		
    		}
    	}
    }
    
	//Remover No(s) (Chamado pelo ToolBar)
    public void DeleteEdge() {
    	if (!this.StackNode.isEmpty()) {
    		GraphNode node1 = GraphNodeStack.showpop(this.StackNode);
    		GraphNode node2 = GraphNodeStack.showpop(this.StackNode);
    		if ((node1 != null) && (node2 != null)) {
    			AdjacentEntry[] allEdges1 = node1.getAllAdjacentNodes();
    			AdjacentEntry[] allEdges2 = node2.getAllAdjacentNodes();
    			for (AdjacentEntry e1: allEdges1) {
    				for (AdjacentEntry e2: allEdges2) {
    					if (e1.getEdge() == e2.getEdge()) {
    		        		removeOneEdgeComponent(e1.getEdge());
    		        		
        					break;
    					}
    				}
    			}
    			this.setEventNone();
    		}
    	}
    	
	}
	

/*-------------------------------------------------*/
/*---------------------TECLADO---------------------*/
/*-------------------------------------------------*/
	public void onEnter() {
		getMTApplication().registerKeyEvent(this);
	}
	
	public void onLeave() {	
		getMTApplication().unregisterKeyEvent(this);
	}
	
    public void setTextInputListener(ITextInputListener listener) {
		this.txtInputListener = listener;
	}
	
	public void keyEvent(KeyEvent e){
		
		if (txtInputListener == null) return;
		int evtID = e.getID();
		
		if (evtID != KeyEvent.KEY_PRESSED) return;
		
		if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) 
			txtInputListener.removeLastCharacter();
		else {
			char c = e.getKeyChar();
			if (c != KeyEvent.CHAR_UNDEFINED)
			txtInputListener.appendCharByUnicode(String.valueOf(c));
		}
		super.drawAndUpdate(getMTApplication().getPGraphics(), 0);	
	}

	public Configuration getConfig() {
		return config;
	}

	public void setConfig(Configuration config) {
		this.config = config;
	}
	
	public void setScaleFactor(float sf) {
		graphArea.setScaleFactor(sf);
	}

	public MTToolbarButton getClickedTBB() {
		return clickedTBB;
	}

	public void setClickedTBB(MTToolbarButton clickedTBB) {
		this.clickedTBB = clickedTBB;
	}
}